/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.skatiger.jrove;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

import javax.el.ELException;

import org.skatiger.jrove.compiler.Compiler;
import org.skatiger.jrove.compiler.SAXCompiler;
import org.skatiger.jrove.component.UIViewRoot;
import org.skatiger.jrove.impl.DefaultJroveFactory;
import org.skatiger.jrove.impl.HtmlResponseWriterImpl;
import org.skatiger.jrove.util.AssertUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MockViewHandler {
    private static final String DEFAULT_SUFFIX = ".xhtml";

    protected static class NullWriter extends Writer {

        static final NullWriter Instance = new NullWriter();

        @Override
        public void close() {
        }

        @Override
        public void flush() {
        }

        @Override
        public void write(char[] buffer) {
        }

        @Override
        public void write(char[] buffer, int off, int len) {
        }

        @Override
        public void write(int c) {
        }

        @Override
        public void write(String str) {
        }

        @Override
        public void write(String str, int off, int len) {
        }
    }

    protected final static Logger logger = LoggerFactory
            .getLogger(MockViewHandler.class);

    private JroveFactory jroveFactory;
    private Writer writer;
    private Configurator configurator;

    /**
     *
     */
    public MockViewHandler(Configurator configurator, Writer writer) {
        this.configurator = configurator;
        this.writer = writer;
    }

    protected void buildView(ViewContext context, UIViewRoot viewToRender) throws IOException,
            JroveException {
        if (jroveFactory == null) {
            initialize();
        }
        // setup our viewId
        String renderedViewId = this.getRenderedViewId(context,
                viewToRender.getViewId());
        viewToRender.setViewId(renderedViewId);

        if (logger.isInfoEnabled()) {
            logger.info("Building View: " + renderedViewId);
        }

        Jrove j = this.jroveFactory.getJrove(viewToRender.getViewId());

        // populate UIViewRoot
        long time = System.currentTimeMillis();
        j.apply(context, viewToRender);
        time = System.currentTimeMillis() - time;
        if (logger.isInfoEnabled()) {
            logger.info("Took " + time + "ms to build view: "
                    + viewToRender.getViewId());
        }
    }

    protected Compiler createCompiler() {
        return new SAXCompiler();
    }

    protected JroveFactory createJroveFactory(Configurator config, Compiler c) {
        return new DefaultJroveFactory(c, config);
    }

    protected ResponseWriter createResponseWriter(ViewContext context) throws IOException,
            JroveException {
        String contentType = "text/html";
        String encoding = "UTF-8";

        ResponseWriter writer;
        writer = new HtmlResponseWriterImpl(NullWriter.Instance, contentType,
                encoding);
        writer = writer.cloneWithWriter(writer);
        return writer;
    }

    public UIViewRoot createView(ViewContext context, String viewId) {
        UIViewRoot viewRoot = new UIViewRoot(viewId);
        viewRoot.setParent(null);
        return viewRoot;
    }

    public String getDefaultSuffix(ViewContext context) throws JroveException {
        return DEFAULT_SUFFIX;
    }

    protected String getRenderedViewId(ViewContext context, String actionId) {
        String viewId = actionId;
        logger.info("ActionId -> ViewId: " + actionId + " -> " + viewId);
        return viewId;
    }

    protected void handleRenderException(ViewContext context, Exception e) throws IOException,
            ELException,
            JroveException {
        UIViewRoot root = context.getViewRoot();

        // always logger
        if (logger.isErrorEnabled()) {
            StringBuffer sb = new StringBuffer(64);
            sb.append("Error Rendering View");
            if (root != null) {
                sb.append('[');
                sb.append(root.getViewId());
                sb.append(']');
            }
            logger.error(sb.toString(), e);
        }
        context.complete();
    }

    /**
     * Initialize the ViewHandler during its first request.
     */
    protected void initialize() {
        synchronized (this) {
            if (this.jroveFactory == null) {
                AssertUtil.notNull(configurator);
                if (logger.isInfoEnabled()) {
                    logger.info("Initializing View Handler");
                }
                Compiler c = this.createCompiler();
                c.initialize(configurator);
                this.jroveFactory = this.createJroveFactory(configurator, c);

                logger.info("Initialization Successful");
            }
        }
    }

    public void renderView(ViewContext context, UIViewRoot viewToRender) throws IOException,
            Exception {

        // exit if the view is not to be rendered
        if (!viewToRender.isRendered()) {
            return;
        }

        // logger request
        logger.debug("Rendering View: " + viewToRender.getViewId());

        try {
            if (viewToRender.getChildren().isEmpty()) {
                this.buildView(context, viewToRender);
            }

            ResponseWriter origWriter = this.createResponseWriter(context);
            ResponseWriter writer = origWriter.cloneWithWriter(origWriter);
            context.setResponseWriter(writer);

            long time = System.currentTimeMillis();
            writer.startDocument();
            viewToRender.encodeAll(context);

            writer.endDocument();
            writer.close();

            time = System.currentTimeMillis() - time;
            if (logger.isDebugEnabled()) {
                logger.debug("Took " + time + "ms to render view: "
                        + viewToRender.getViewId());
            }
            context.complete();
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
            throw fnfe;
        } catch (Exception e) {
            this.handleRenderException(context, e);
            throw e;
        } finally {
        }
    }

}
